from functools import wraps
from typing import Optional
import time, json
from datetime import datetime
from fastapi import Request
from fastapi.responses import JSONResponse, ORJSONResponse, UJSONResponse
from app.service.system import SysLogService


def Log(
    title: str,
    business_type: int,
    log_type: Optional[str] = 'operation'
):
    """
    日志装饰器
    :param log_type: 日志类型（login表示登录日志，默认或者operation表示为操作日志）
    :param title: 当前日志装饰器装饰的模块标题
    :param business_type: 业务类型（10其它 1新增 2修改 3删除 4授权 5导出 6导入 7强退 8生成代码 9清空数据 100：登录成功   101：退出成功  102：验证码错误  103：账号密码错误
）
    :return:
    """

    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):

            start_time = time.time()  # 调用开始
            request: Request = kwargs.get('req')
            user_agent = request.headers.get('User-Agent')

            # 获取请求的ip
            oper_ip = request.client.host

            # 根据不同的请求类型使用不同的方法获取请求参数
            content_type = request.headers.get("Content-Type")

            if log_type == 'operation':
                oper_url = request.url.path
                oper_method = request.method
                if content_type and ("multipart/form-data" in content_type or 'application/x-www-form-urlencoded' in content_type):
                    payload = await request.form()
                    oper_param = "\n".join([f"{key}: {value}" for key, value in payload.items()])
                else:
                    payload = await request.body()
                    if not payload:
                        oper_param = ''
                    else:
                        oper_param = json.dumps(json.loads(str(payload, 'utf-8')), ensure_ascii=False)
                        # 日志表请求参数字段长度最大为2000，因此在此处判断长度
                if len(oper_param) > 2000:
                    oper_param = '请求参数过长'

            # 获取操作时间
            oper_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

            # 调用原始函数
            result = await func(*args, **kwargs)

            # 获取请求耗时
            cost_time = round((time.time() - start_time) * 1000)

            # 根据响应结果的类型使用不同的方法获取响应结果参数
            if isinstance(result, JSONResponse) or isinstance(result, ORJSONResponse) or isinstance(result, UJSONResponse):
                result_dict = json.loads(str(result.body, 'utf-8'))
            else:
                result_dict = {'code': result['code'], 'msg': result['msg']}

            json_result = json.dumps(dict(code=result_dict.get('code'), msg=result_dict.get('msg')), ensure_ascii=False)

            auth = kwargs.get('auth')

            # 根据日志类型向对应的日志表插入数据
            service = await SysLogService.instance(request)

            logData = {
                'ip': oper_ip,
                'log_type': 0 if log_type=='login' else 1,
                'login_status': 1 if result['code']==200 else 0,
                'business_type': business_type,
                'user_agent': user_agent,
                'create_time': oper_time,
                'result_msg': json_result,
                'title': title,
                'duration': cost_time,
                'username': "位置" if auth is None else auth.username,
                'module': ''
            }

            if log_type == 'login':
                logData['username'] = "未知" if auth is None else auth.username

                await service.insert(logData)
            else:
                logData['username'] = request.app.state.user['username']
                logData['req_uri'] = oper_url
                logData['req_method'] = oper_method
                logData['req_params'] = oper_param
                await service.insert(logData)

            return result

        return wrapper

    return decorator
